import type { Plugin, ResolvedConfig, ResolvedServerOptions, UserConfig, ViteDevServer } from 'vite'
import fs from 'node:fs'
import path from 'node:path'
import chokidar from 'chokidar'

import { createBootConfig } from './create-boot'
import { getDeclaration } from './declaration'
import { readAndParseEnv } from './parse-env'
import { ROOT_PATH } from './utils'

interface IBootConfig {
  /** @description  根目录 默认取process.cwd() */
  root?: string
  /** @description  package.json中配置的bootName, 默认为BOOT_ENV */
  bootName?: string
  /** @description  环境中的基础路径字段, */
  // envBaseUrlFields: string | string[]
  envDir?: string
  /** @description  是否启用代理字段, */
  proxyFields?: string
  /** @description  环境变量类型.js 或 .env */
  envType?: 'ts' | 'env'
  /** @description  启动变量输出目录 */
  bootDirName?: string
  /** @description  其它平台使用h5的配置 */
  platAsH5?: string[]
  /** @description  h5平台名称, 以process.env.UNI_PLATFORM名为准 */
  h5platName?: string
  /** @description  本地个人环境配置，生产环境不会打入，如 .env.myself 则该变量输入myself */
  privateEnvName?: string
}
const virtualModuleId = 'virtual:mui-env'
const resolvedVirtualModuleId = `\0${virtualModuleId}`
export function uniBoot(userOptions: IBootConfig = {}): Plugin {
  const { root = ROOT_PATH } = userOptions
  // eslint-disable-next-line node/prefer-global/process
  const { UNI_PLATFORM = '', NODE_ENV = 'production' } = process.env
  console.log(`UNI_PLATFORM=${UNI_PLATFORM};NODE_ENV=${NODE_ENV}`) // 得到 mp-weixin, h5, app 等
  /* 读取指定环境变量目录下的环境变量 */
  const envDirPath = path.join(root, userOptions.envDir || 'env')
  let gConfig: UserConfig
  return {
    name: 'vite-plugin-uni-boot',
    enforce: 'pre',
    configureServer(serve: ViteDevServer) {
      serverWatch(envDirPath, async (_e) => {
        await genBootConfig(gConfig, userOptions, { UNI_PLATFORM, NODE_ENV, envDirPath })
        serve.restart()
      })
    },
    async config(config: UserConfig) {
      gConfig = config
      await genBootConfig(config, userOptions, { UNI_PLATFORM, NODE_ENV, envDirPath })
      return config
    },
    transformIndexHtml(html) {
      // h5环境增加编译时间
      if (UNI_PLATFORM === 'h5') {
        return html.replace('%BUILD_DATE%', new Date().toLocaleString())
      }
    },
    resolveId(id) {
      if (id === virtualModuleId) {
        return resolvedVirtualModuleId
      }
    },
    async load(id) {
      if (id === resolvedVirtualModuleId) {
        const envStr = `export const env = ${gConfig.define!.__APP_ENV__};`
        const localEnv = `export const localEnv = ${gConfig.define!.__APP_LOCAL_ENV__};`
        const bootConfigStr = `export const bootConfig = ${gConfig.define!.__APP_BOOT_CONFIG__};`
        return [envStr, localEnv, bootConfigStr].join('\n')
      }
    },
  }
}
let isWatch = false
function serverWatch(envDirPath: string, cb: ((arg0: string) => any) | undefined) {
  if (isWatch) return
  isWatch = true
  const watcher = chokidar.watch(envDirPath, {
    persistent: true, // 保持监控状态
    ignoreInitial: false, // 初始扫描时触发事件
    depth: 1, // 监控目录的深度
    interval: 1000, // 轮询间隔（毫秒）
  })
  watcher.on('change', (event) => {
    cb && cb(event)
  })
}
async function genBootConfig(config: UserConfig, userOptions: IBootConfig, { UNI_PLATFORM, NODE_ENV, envDirPath }: Record<string, string>) {
  const { root = ROOT_PATH, bootName = 'BOOT_ENV', envType = 'ts', privateEnvName = 'localhost', bootDirName = 'src/.boot', h5platName = 'h5', platAsH5 = ['app'] } = userOptions

  if (!config.define) config.define = {}

  let { devMap, comaMap } = await readAndParseEnv(envDirPath, envType) // all .env files

  /* ================================================= */
  const plat = UNI_PLATFORM // 实际打包的真正平台名,app会使用h5的启动配置,因为app运行时不能携带任何启动参数
  const nodeEnv = NODE_ENV // node自身真正的环境变量
  const isDev = nodeEnv === 'development'
  const isPro = nodeEnv === 'production'

  const envName = parseEnvArg(bootName)

  if (envName?.includes('production')) {
    const production = devMap[envName]
    devMap = { [envName]: production || {} }
  }

  // 个人私有配置，正式不会发布
  if (isPro || envName?.includes('production')) { // node环境为正式或者请求地址为正式都需要删除个人私有配置
    devMap[privateEnvName] && (delete devMap[privateEnvName])
  }

  config.define.__MUI_ENV_CONFIG__ = JSON.stringify(devMap)
  let localEnv = {}
  let localEnvComaMap = {}
  if (isDev || !envName?.includes('production')) { // 测试环境输出私有配置
    localEnv = devMap[privateEnvName] || {}
    localEnvComaMap = comaMap[privateEnvName] || {}
  }
  if (envName) { // 输出环境变量配置文件
    const bootConfig = { plat, rawPlat: UNI_PLATFORM, nodeEnv, isDev, envName }
    const globalEnv = { root, env: devMap[envName], envComa: comaMap[envName], localEnv, localEnvComaMap, bootConfig }
    createGlobalEnv(config, globalEnv)
    createBootConfig(root, bootConfig, bootDirName, `${plat}.json`)
  }
  else if (platAsH5.includes(plat)) {
    // 没有任何启动参数则取h5的配置,
    const bootName = path.join(root, bootDirName, `${h5platName}.json`)

    if (!fs.existsSync(bootName)) {
      const globalEnv = { root, env: {}, envComa: {}, localEnv, localEnvComaMap, bootConfig: {} }
      console.log(`${bootName}启动名不存在`)
      createGlobalEnv(config, globalEnv)
      return
    }
    const bootConfig = readBootConfig(bootName)

    const newBootConfig = { plat: bootConfig.plat, rawPlat: UNI_PLATFORM, nodeEnv, isDev, envName: bootConfig.envName }
    const globalEnv = { root, env: devMap[bootConfig.envName], envComa: comaMap[bootConfig.envName], localEnv, localEnvComaMap, bootConfig: newBootConfig }
    createGlobalEnv(config, globalEnv)
  }
  else {
    const globalEnv = { root, env: {}, envComa: {}, localEnv, localEnvComaMap, bootConfig: {} }
    console.log(`${bootName}启动名不存在`)
    createGlobalEnv(config, globalEnv)
  }
}

function createGlobalEnv(config: UserConfig, { root, env, envComa, localEnv, localEnvComaMap, bootConfig }: Record<string, any>) {
  config.define!.__APP_ENV__ = JSON.stringify(env)
  config.define!.__APP_LOCAL_ENV__ = JSON.stringify(localEnv)
  config.define!.__APP_BOOT_CONFIG__ = JSON.stringify(bootConfig)
  setTimeout(() => {
    getDeclaration({ root, env, envComa, localEnv, localEnvComaMap })
  }, 100)
}

function readBootConfig(path: string): Record<string, string> {
  try {
    const content = fs.readFileSync(path, 'utf8')
    return JSON.parse(content) || {}
  }
  catch (error) {
    return {}
  }
}
function parseEnvArg(bootName: string) {
  // eslint-disable-next-line node/prefer-global/process
  const packageJsonScript = process.env.UNI_CUSTOM_DEFINE || '' // hbuilderx自定义参数
  // eslint-disable-next-line node/prefer-global/process
  const bootEnvStr = process.env.npm_config_mode // cli 命令自己输入的变量
  try {
    return JSON.parse(packageJsonScript)?.[bootName] || bootEnvStr || ''
  }
  catch (error) {
    return bootEnvStr || ''
  }
}
function delayTime(time: number) {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve(true)
    }, time)
  })
}
export default uniBoot
